﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/8 14:09:04
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK.Exceptions;
using ImageK.IO;
using ImageK.Macros;

namespace ImageK
{
    /// <summary>
    /// The class contains static methods that perform macro operations.
    /// </summary>
    public class Macro
    {
		public static string MACRO_CANCELED = "Macro canceled";

	    // A table of Thread as keys and string as values, so  
	    // Macro options are local to each calling thread.
	    static private Hashtable table = new Hashtable();
		public static bool _abort;

        public Macro()
        {
        }

        public static bool open(string path)
        {
            Opener o;
			if (path == null || path.Equals(""))
			{
                o = new Opener();
				return true;
			}
			o = new Opener();
			ImagePlus img = o.openImage(path);
			if (img == null)
				return false;
			img.Show();
			return true;
		}

		public static bool saveAs(string path)
		{
			ImagePlus imp = WindowManager.getCurrentImage();
			if (imp == null)
				return false;
			FileSaver fs = new FileSaver(imp);
			if (path == null || path.Equals(""))
				return fs.saveAsTiff();
			if (imp.getStackSize() > 1)
				return fs.saveAsTiffStack(path);
			else
				return fs.saveAsTiff(path);
		}

        /** Aborts the currently running macro or any plugin using IJ.run(). */
		public static void abort()
		{
			//IJ.log("Abort: "+Thread.currentThread().getName());
			_abort = true;
			if (Thread.CurrentThread.Name!.EndsWith("Macro$"))
			{
				table.Remove(Thread.CurrentThread);
				throw new RuntimeException(MACRO_CANCELED);
			}
		}

		/** If a command started using run(name, options) is running,
			and the current thread is the same thread,
			returns the options string, otherwise, returns null.
			@see ij.gui.GenericDialog
			@see ij.io.OpenDialog
		*/
		public static string? GetOptions()
		{
			string? threadName = Thread.CurrentThread.Name;
            if (threadName == null)
            {
                return null;
            }
			//IJ.log("getOptions: "+threadName+" "+Thread.currentThread().hashCode()); //ts
			if (threadName.StartsWith("Run$_") || threadName.StartsWith("RMI TCP"))
			{
				object? options = table[Thread.CurrentThread];
				return options == null ? null : options + " ";
			}
			else
				return null;
		}

		/** Define a set of Macro options for the current Thread. */
		public static void setOptions(string options)
		{
			//IJ.log("setOptions: "+Thread.currentThread().getName()+" "+Thread.currentThread().hashCode()+" "+options); //ts
			if (options == null || options.Equals(""))
				table.Remove(Thread.CurrentThread);
            else
            {
                if (table.Contains(Thread.CurrentThread))
                {
                    table[Thread.CurrentThread] = options;
				}
                else
                {
                    table.Add(Thread.CurrentThread, options);
                }
            }
				
		}

		/** Define a set of Macro options for a Thread. */
		public static void setOptions(Thread thread, string options)
		{
			if (null == thread)
				throw new RuntimeException("Need a non-null thread instance");
			if (null == options)
				table.Remove(thread);
            else
            {
                if (table.Contains(thread))
                {
                    table[thread] = options;
                }
                else
                {
                    table.Add(thread, options);
                }
			}
		}

		public static string getValue(String options, string key, string defaultValue)
		{
			key = trimKey(key);
			if (!options.EndsWith(" "))
				options = options + " ";
			key += '=';
			int index = -1;
			do
			{ // Require that key not be preceded by a letter
				index = options.IndexOf(key, ++index);
				if (index < 0) return defaultValue;
			} while (index != 0 && Char.IsLetter(options[index - 1]));
			options = options.Substring(index + key.Length, options.Length);
			if (options[0] == '\'')
			{
				index = options.IndexOf("'", 1);
				if (index < 0)
					return defaultValue;
				else
					return options.Substring(1, index);
			}
			else if (options[0] == '[')
			{
				int count = 1;
				index = -1;
				for (int i = 1; i < options.Length; i++)
				{
					char ch = options[i];
					if (ch == '[')
						count++;
					else if (ch == ']')
						count--;
					if (count == 0)
					{
						index = i;
						break;
					}
				}
				if (index < 0)
					return defaultValue;
				else
					return options.Substring(1, index);
			}
			else
			{
				index = options.IndexOf(" ");
				if (index < 0)
					return defaultValue;
				else
					return options.Substring(0, index);
			}
		}

		public static string trimKey(String key)
		{
			int index = key.IndexOf(" ");
			if (index > -1)
				key = key.Substring(0, index);
			index = key.IndexOf(":");
			if (index > -1)
				key = key.Substring(0, index);
			// key = key.ToLower(Locale.US);
			key = key.ToLower();
			return key;
		}

		/** Evaluates 'code' and returns the output, or any error, 
		 * as a string (e.g., Macro.eval("2+2") returns "4").	
		*/
		public static string eval(String code)
		{
			return new Interpreter().eval(code);
		}

	}
}
